/*---------------------------------------------------------------------------
// 
//  File:  hc.c
//  Purpose:  Host Universal Architecture: hc resource
//  History:  2013-08-26 Created by  
//
---------------------------------------------------------------------------*/
#include <stdio.h>
#include <memory.h>
#include "sm_trace.h"
#include "itc.h"
#include "app_proxy.h"
#include "hc.h"
#include "../common/sm_kapi.h"
#include "../cistack/xmsg.h"


typedef struct  
{
	unsigned char					m_u8Init;
	unsigned char					m_u8Run;
	unsigned char					m_u8ExitFlag;
	unsigned int				m_u32HcTaskid;
	sm_sem_t				m_semHcTask;
	sm_sem_t				m_semLock;
}hc_data_t;

hc_data_t s_stHc = {0};

static unsigned char s_hcConnected;
unsigned long hctest_taskID;
static unsigned char s_isHcSessOpened;
static unsigned short s_hcSsnb;

static CiHcChangeChannel CiHostControlTuneFun = NULL;
	
int hc_task(void* pParam);
static int HcCheckSessActive(unsigned short u16SlotId, short u16Ssnb);


void CiHcSetChangeChannelFun(CiHcChangeChannel func)
{
	CiHostControlTuneFun = func;
}


void SendHcResourceAPDU(unsigned char u8CISlot, unsigned short u16SessionNB,
							 const unsigned char*pu8APDUData, unsigned int u32APDULen)//Send APDU of Private Resource to CICAM. 
{
	xmsg_t		xmsg;
	int			apduLen;

	memset(&xmsg, 0x00, sizeof(xmsg));
	apduLen = u32APDULen;
	xmsg.m_bd.m_app_apdu.m_pbApdu = ITC_DynAlloc(apduLen);
	if (xmsg.m_bd.m_app_apdu.m_pbApdu == NULL)
	{
		SM_TRACE_INFO(TRMOD_HC,"SendHcResourceAPDU() - mem alloc error");
		return;
	}

	xmsg.m_sndrcver = (RM << 4) | RM;
	xmsg.m_type  = APP_APDU;
	xmsg.m_bd.m_app_apdu.m_ssnb = u16SessionNB;
	xmsg.m_bd.m_app_apdu.m_apduLen = apduLen;
	memcpy(xmsg.m_bd.m_app_apdu.m_pbApdu, pu8APDUData, apduLen);

	ITC_SendXmsg(&xmsg);
}



/******************************************************************************
  函数名: HcSessParseAPDU()
  描述:   分析接收到CAM发送过来的HC APDU
  其他:
******************************************************************************/
static int HcSessParseAPDU(unsigned char u8SlotId, short u16Ssnb, unsigned char* pu8Apdu, unsigned int u32Len)
{
	unsigned int u32SessIndex;
	unsigned int u32ParseIndex;
	unsigned int u32Tag;
	unsigned long u32ApduLen;
	unsigned short netId, tsId, serviceId;

	SM_TRACE_ENTRY(TRMOD_HC, "HcSessParseAPDU entry u8SlotId = %d, u16Ssnb = %d, pu8Apdu = %p, u32Len = %d\r\n", u8SlotId, u16Ssnb, pu8Apdu, u32Len);
	if(u8SlotId > MAXSLOTID)
	{
		SM_TRACE_ERROR(TRMOD_HC, "HcSessParseAPDU found u32SlotId error\r\n");
		return -1;
	}

	SM_OsSemWait(&s_stHc.m_semLock, -1);

	if(s_isHcSessOpened == 1)
	{
		u32Tag = asnReadInt24(pu8Apdu);
		u32ParseIndex = 3;
		u32ParseIndex += asnReadApduLengthField(&pu8Apdu[3], 4, &u32ApduLen);
		switch(u32Tag)
		{
		case CI_HC_TAG_TUNE:
			SM_TRACE_NOTIFY(TRMOD_HC, "HcSessParseAPDU receive HC_TUNE\r\n");
			if(u32ApduLen == 8)
			{
				netId = (pu8Apdu[u32ParseIndex]<<8) + pu8Apdu[u32ParseIndex+1];
				tsId = (pu8Apdu[u32ParseIndex+4]<<8) + pu8Apdu[u32ParseIndex+5];
				serviceId = (pu8Apdu[u32ParseIndex+6]<<8) + pu8Apdu[u32ParseIndex+7];

				SM_TRACE_NOTIFY(TRMOD_HC, "HcSessParseAPDU() - NID[%d], TSId[%d], SID[%d]\r\n", netId, tsId, serviceId);

				if(CiHostControlTuneFun != NULL)
					CiHostControlTuneFun(netId, tsId, serviceId);
			}		
			break;
		default:
			SM_TRACE_ERROR(TRMOD_HC, "HcSessParseAPDU receive error APDU tag\r\n");
			break;
		}
	}

	SM_OsSemRelease(&s_stHc.m_semLock);
	SM_TRACE_RETURN(TRMOD_HC, "HcSessParseAPDU return\r\n");
	return 0;
}



void HcAPDURecv(unsigned char u8CISlot, unsigned short u16SessionNB, unsigned char *pu8RxAPDU)
{
	unsigned long u32BodyLen;
	unsigned int u32LenSize;
	hc_msg_t stMsg;

	SM_TRACE_ENTRY(TRMOD_HC, "HcAPDURecv entry, u8CISlot = %d, u16SessionNB = %d, pu8RxAPDU = %p\r\n", u8CISlot, u16SessionNB, pu8RxAPDU);
	stMsg.m_u8Slotnb = u8CISlot;
	stMsg.m_u16Ssnb = u16SessionNB;
	u32LenSize = asnReadApduLengthField(pu8RxAPDU+3, 4, &u32BodyLen);
	stMsg.m_pu8Apdu = (unsigned char*)malloc(u32BodyLen+3+u32LenSize);
	if(stMsg.m_pu8Apdu)
	{
		memcpy(stMsg.m_pu8Apdu, pu8RxAPDU, u32BodyLen+3+u32LenSize);
		stMsg.m_u32ApduLen = u32BodyLen+3;
		stMsg.m_eType = CI_HC_MESSAGE_TYPE_DATA;
		SM_SendQueueByTaskid(s_stHc.m_u32HcTaskid, &stMsg); 
	}
}

void HcSessionCloseReq(unsigned char u8CISlot, unsigned short u16SessionNB)
{
	hc_msg_t stMsg;

	SM_TRACE_ENTRY(TRMOD_HC, "HcSessionCloseReq entry, u8CISlot = %d, u16SessionNB = %d\r\n", u8CISlot, u16SessionNB);
	
	stMsg.m_u8Slotnb = u8CISlot;
	stMsg.m_u16Ssnb = u16SessionNB;
	stMsg.m_eType = CI_HC_MESSAGE_TYPE_CLOSE;
	SM_SendQueueByTaskid(s_stHc.m_u32HcTaskid, &stMsg);

	SM_TRACE_RETURN(TRMOD_HC, "HcSessionCloseReq return\r\n");
}



/******************************************************************************
  函数名: hc_init()
  描述:   注册回调、创建HC相关线程
  其他:
******************************************************************************/
int hc_init(void)
{
	SM_TRACE_ENTRY(TRMOD_HC, "hc_init() - entry\r\n");

	if(s_stHc.m_u8Init == 0)
	{
		memset(&s_stHc, 0, sizeof(s_stHc));
		SM_OsSemCreate(&s_stHc.m_semLock, NULL, 1);
		s_stHc.m_u32HcTaskid = SM_CreateTask("hc", hc_task, NULL, NULL, 20*1024, 81, TRUE, HC_TASK_AID);
		s_stHc.m_u8Init = 1;
		SM_TRACE_INFO(TRMOD_HC, "hc_init finish\r\n");
	}
	else
	{
		SM_TRACE_ERROR(TRMOD_HC, "hc_init have run\r\n");
		return 1;
	}
	
	SM_TRACE_RETURN(TRMOD_HC, "hc_init() - return\r\n");
	return 0;
}


int hc_on_message(xmsg_t* pXmsg)
{
	xmsg_t xmsg;
	unsigned short ssnb;
	unsigned char tcid;

	switch (pXmsg->m_type)
	{
	case APP_OPEN_SS_REQ:
		ssnb = pXmsg->m_bd.m_app_open_ss_req.m_ssnb;
		tcid = pXmsg->m_bd.m_app_open_ss_req.m_tcid;
		SM_TRACE_INFO(TRMOD_HC, "hc_on_message() - APP_OPEN_SS_REQ received, ssnb=%d\r\n", ssnb);
		memset(&xmsg,0x00,sizeof(xmsg_t));
		xmsg.m_sndrcver = (RM << 4) | RM;
		xmsg.m_type = APP_OPEN_SS_RSP;
		xmsg.m_bd.m_app_open_ss_rsp.m_tcid = tcid;
		xmsg.m_bd.m_app_open_ss_rsp.m_ssnb = ssnb;
		xmsg.m_bd.m_app_open_ss_rsp.m_ressId = pXmsg->m_bd.m_app_open_ss_req.m_ressId;
		xmsg.m_bd.m_app_open_ss_rsp.m_status = OK; //HcSessionOpenReq(0, ssnb);
		
		if(0==s_isHcSessOpened)
		{
			s_hcSsnb=ssnb;
			s_isHcSessOpened = 1;
		}

		SM_TRACE_INFO(TRMOD_HC, "hc_on_message() - will send APP_OPEN_SS_RSP to PROXY\r\n");
		ITC_SendXmsg(&xmsg);

		break;
	case APP_SS_BROKEN:
		ssnb = pXmsg->m_bd.m_app_ss_broken.m_ssnb;
		HcSessionCloseReq(0, ssnb);
		break;
	case APP_TMPEND:
		ssnb = (unsigned short)pXmsg->m_bd.m_timer.m_param;
		memset(&xmsg, 0x00, sizeof(xmsg));
		xmsg.m_sndrcver = (RM << 4) | RM;
		xmsg.m_type  = APP_CLOSE_SS_REQ;
		xmsg.m_bd.m_app_close_ss_req.m_ssnb = ssnb;
		SM_TRACE_INFO(TRMOD_HC, "hc_on_message() - is timer event, will send APP_CLOSE_SS_REQ to PROXY, ssnb=%d\r\n", ssnb);
		HcSessionCloseReq(0, ssnb);
		ITC_SendXmsg(&xmsg);
		break;
	case APP_APDU:
		HcAPDURecv(0, pXmsg->m_bd.m_app_apdu.m_ssnb, pXmsg->m_bd.m_app_apdu.m_pbApdu);
		break;
	default:
		break;
	}
	return 0;
}


int hc_task(void* pParam)
{
	unsigned int rc = 0;
	unsigned int u32MsgLen;
	hc_msg_t stMsg;

	SM_TRACE_ENTRY(TRMOD_HC, "hc_task entry pParam = %p\r\n", pParam);

	SM_TRACE_INFO(TRMOD_HC, "hc_task start\r\n");
	while(s_stHc.m_u8ExitFlag != 1)
	{
		memset(&stMsg, 0, sizeof(stMsg));
		if(SM_ReceiveQueueByTaskid(s_stHc.m_u32HcTaskid, &stMsg, -1))
		{
			switch(stMsg.m_eType)
			{
			case CI_HC_MESSAGE_TYPE_DATA:
				HcSessParseAPDU(stMsg.m_u8Slotnb, stMsg.m_u16Ssnb, stMsg.m_pu8Apdu, stMsg.m_u32ApduLen);
				free(stMsg.m_pu8Apdu);
				break;
			case CI_HC_MESSAGE_TYPE_CLOSE://application close
				SM_TRACE_ERROR(TRMOD_HC, "hc_task receive close session APDU, stMsg.m_u8Slotnb = %d, stMsg.m_u16Ssnb = %d\r\n", stMsg.m_u8Slotnb, stMsg.m_u16Ssnb);
				s_isHcSessOpened = 0;
				s_hcSsnb = 0;
				break;
			default:
				SM_TRACE_ERROR(TRMOD_HC, "hc_task recv unknowed msg\r\n");
				break;
			}
		}
		else
		{
			SM_TRACE_ERROR(TRMOD_HC, "hc_task recv msg error\r\n");
		}
	}

	SM_TRACE_RETURN(TRMOD_HC, "hc_task return\r\n");
	return 0;
}


